package com.haizhi.workstation.util;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public final class HttpClientUtils {

    public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
    public static final Charset US_ASCII = Charset.forName("US-ASCII");
    public static final Charset UTF_16 = Charset.forName("UTF-16");
    public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
    public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
    public static final Charset UTF_8 = Charset.forName("UTF-8");
    public static final Charset GBK = Charset.forName("GBK");

    // 设置连接池线程最大数量
    static final int MAX_TOTAL = 500;
    // 设置单个路由最大的连接线程数量
    static final int DEFAULT_MAX_PER_ROUTE = 50;
    // 连接超时
    static final int CONNECT_TIMEOUT = 3000;
    // 数据读取超时时间，即SocketTimeout
    static final int READ_TIMEOUT = 3000;

    // 重试次数
    static final int RETRY = 3;

    static final HttpClient httpClient;

    static RequestConfig GLOBAL_CONFIG = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT).build();

    static {
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(MAX_TOTAL);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);

        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

        httpClient = httpClientBuilder.build();
    }

    public static String doGet(String url) {
        return doGet(url, UTF_8, CONNECT_TIMEOUT, READ_TIMEOUT, null, null);
    }

    public static String doGet(String url, Integer retry) {
        return doGet(url, UTF_8, CONNECT_TIMEOUT, READ_TIMEOUT, null, retry);
    }

    public static String doGet(String url, int connectTimeout, int socketTimeout) {
        return doGet(url, UTF_8, connectTimeout, socketTimeout, null, null);
    }

    public static String doGet(String url, int connectTimeout, int socketTimeout, Integer retry) {
        return doGet(url, UTF_8, connectTimeout, socketTimeout, null, retry);
    }

    public static String doGet(String url, Charset charset) {
        return doGet(url, charset, CONNECT_TIMEOUT, READ_TIMEOUT, null, null);
    }

    public static String doGet(String url, Charset charset, int connectTimeout, int socketTimeout, Map<String, String> headers, Integer retry) {
        URI URI = convertURI(url);
        if (null == URI) {
            return null;
        }

        HttpGet get = new HttpGet(URI.toString());
        RequestConfig.Builder builder = RequestConfig.copy(GLOBAL_CONFIG);
        builder.setConnectTimeout(connectTimeout);
        builder.setSocketTimeout(socketTimeout);
        get.setConfig(builder.build());

        setHeader(get, headers);

        return doHttp(get, charset, retry);
    }

    public static String doPost(String url) {
        return doPost(url, UTF_8, CONNECT_TIMEOUT, READ_TIMEOUT, null, null, null);
    }

    public static String doPost(String url, Integer retry) {
        return doPost(url, UTF_8, CONNECT_TIMEOUT, READ_TIMEOUT, null, null, retry);
    }

    public static String doPost(String url, int connectTimeout, int socketTimeout) {
        return doPost(url, UTF_8, connectTimeout, socketTimeout, null, null, null);
    }

    public static String doPost(String url, int connectTimeout, int socketTimeout, Integer retry) {
        return doPost(url, UTF_8, connectTimeout, socketTimeout, null, null, retry);
    }

    public static String doPost(String url, Map<String, String> params) {
        return doPost(url, UTF_8, CONNECT_TIMEOUT, READ_TIMEOUT, params, null, null);
    }

    public static String doPost(String url, Map<String, String> params, Integer retry) {
        return doPost(url, UTF_8, CONNECT_TIMEOUT, READ_TIMEOUT, params, null, retry);
    }

    public static String doPost(String url, Map<String, String> params, int connectTimeout, int socketTimeout) {
        return doPost(url, UTF_8, connectTimeout, socketTimeout, params, null, null);
    }

    public static String doPost(String url, Map<String, String> params, int connectTimeout, int socketTimeout, int retry) {
        return doPost(url, UTF_8, connectTimeout, socketTimeout, params, null, retry);
    }

    public static String doPost(String url, Charset charset) {
        return doPost(url, charset, CONNECT_TIMEOUT, READ_TIMEOUT, null, null, null);
    }

    public static String doPost(String url, Charset charset, Map<String, String> params) {
        return doPost(url, charset, CONNECT_TIMEOUT, READ_TIMEOUT, params, null, null);
    }

    public static String doPost(String url, Charset charset, int connectTimeout, int socketTimeout, Map<String, String> params, Map<String, String> headers,
                                Integer retry) {
        URI URI = convertURI(url);
        if (null == URI) {
            return null;
        }

        HttpPost post = new HttpPost(URI.toString());
        RequestConfig.Builder builder = RequestConfig.copy(GLOBAL_CONFIG);
        builder.setConnectTimeout(connectTimeout);
        builder.setSocketTimeout(socketTimeout);
        post.setConfig(builder.build());

        UrlEncodedFormEntity urlEncodedFormEntity = getUrlEncodedFormEntity(params, charset);
        if (null != urlEncodedFormEntity) {
            post.setEntity(urlEncodedFormEntity);
        }

        setHeader(post, headers);

        return doHttp(post, charset, retry);
    }

    private static String doHttp(HttpRequestBase httpRequestBase, Charset charset, Integer retry) {
        int retryTime = (retry == null ? RETRY : (retry > 0 ? retry.intValue() : RETRY));
        for (int i = 0; i < retryTime; i++) {
            try {
                HttpResponse response = httpClient.execute(httpRequestBase);
                if (null == response) {
                    continue;
                }

                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == HttpStatus.SC_OK) {
                    HttpEntity entity = response.getEntity();
                    return EntityUtils.toString(entity, charset);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (null != httpRequestBase) {
                    httpRequestBase.releaseConnection();
                }
            }
        }

        return null;
    }

    private static URI convertURI(String url) {
        try {
            return new URI(url);
        } catch (URISyntaxException e) {
            try {
                URL URL = new URL(url);
                return new URI(URL.getProtocol(), URL.getUserInfo(), URL.getHost(), URL.getPort(), URL.getPath(), URL.getQuery(), null);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }

        return null;
    }

    private static void setHeader(HttpRequestBase httpRequestBase, Map<String, String> headers) {
        if (MapUtils.isNotEmpty(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                String _headerKey = entry.getKey();
                String _headerValue = entry.getValue();
                if (StringUtils.isEmpty(_headerKey)) {
                    continue;
                }

                httpRequestBase.setHeader(_headerKey, _headerValue);
            }
        }
    }

    private static UrlEncodedFormEntity getUrlEncodedFormEntity(Map<String, String> params, Charset charset) {
        if (MapUtils.isNotEmpty(params)) {
            List<NameValuePair> list = new ArrayList<>(params.size());
            for (Map.Entry<String, String> entry : params.entrySet()) {
                String _paramKey = entry.getKey();
                String _paramValue = entry.getValue();
                if (StringUtils.isBlank(_paramKey)) {
                    continue;
                }

                list.add(new BasicNameValuePair(_paramKey, _paramValue));
            }

            return new UrlEncodedFormEntity(list, charset);
        }

        return null;
    }

    public static String uploadFileImpl(String serverUrl, InputStream inputStream, String fileName)
            throws Exception {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        CloseableHttpResponse httpResponse = null;
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(200000).setSocketTimeout(200000000).build();
        HttpPost httpPost = new HttpPost(serverUrl);
        httpPost.setConfig(requestConfig);
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        multipartEntityBuilder.setCharset(Charset.forName("UTF-8"));

        multipartEntityBuilder.addBinaryBody("file", inputStream, ContentType.MULTIPART_FORM_DATA, fileName);
        HttpEntity httpEntity = multipartEntityBuilder.build();
        httpPost.setEntity(httpEntity);
        httpResponse = httpClient.execute(httpPost);
        HttpEntity responseEntity = httpResponse.getEntity();
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        StringBuffer buffer = new StringBuffer();
        if (statusCode == 200) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));

            String str = "";
            while (!StringUtils.isEmpty(str = reader.readLine())) {
                buffer.append(str);
            }
            System.out.println(buffer.toString());
        } else {
            buffer.append(responseEntity.toString());
        }

        httpClient.close();
        if (httpResponse != null) {
            httpResponse.close();
        }
        return buffer.toString();
    }


    private HttpClientUtils() {
    }
}
